package org.iks.common.util;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.iks.common.ResultCode;
import org.iks.common.constant.Constant;
import org.iks.common.exception.BusinessException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author kzou
 * @date 2023/12/21 20:17
 * @description: servlet 工具类
 */
@Slf4j
public class ServletUtil {
    private static final String UNKNOWN = "unknown";
    private static final String LOCALHOST_IP = "127.0.0.1";
    private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";

    public static Map<String, String> parseGetRequestParameter(HttpServletRequest request, String separator) {
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
            String[] values = entry.getValue();
            String value = String.join(separator);
            result.put(entry.getKey(), value);
        }
        return result;
    }

    public static Map<String, String> parseGetRequestParameter(HttpServletRequest request) {
        return parseGetRequestParameter(request, "&");
    }

    public static String httpRequestIp(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            if (LOCALHOST_IP.equalsIgnoreCase(ip) || LOCALHOST_IPV6.equalsIgnoreCase(ip)) {
                // 根据网卡取本机配置的 IP
                InetAddress iNet = null;
                try {
                    iNet = InetAddress.getLocalHost();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
                if (iNet != null)
                    ip = iNet.getHostAddress();
            }
        }
        // 对于通过多个代理的情况，分割出第一个 IP
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(",") > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return LOCALHOST_IPV6.equals(ip) ? LOCALHOST_IP : ip;
    }

    public static String httpRefererUrl(HttpServletRequest request) {
        return request.getHeader("Referer ");
    }


    @Nullable
    public static HttpServletRequest tryGetHttpServletRequest() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return null;
        }
        return requestAttributes.getRequest();
    }

    @Nullable
    public static String tryGetCookie(String name) {
        HttpServletRequest httpServletRequest = tryGetHttpServletRequest();
        if (httpServletRequest == null) {
            return null;
        }
        return tryGetCookie(httpServletRequest, name);
    }

    @Nullable
    public static String tryGetCookie(@NonNull HttpServletRequest request, String name) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return null;
        }
        for (Cookie cookie : cookies) {
            if (Objects.equals(cookie.getName(), name)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    @Nullable
    public static String tryGetToken() {
        HttpServletRequest httpServletRequest = tryGetHttpServletRequest();
        if (httpServletRequest == null) {
            return null;
        }
        return tryGetToken(httpServletRequest);
    }

    @NonNull
    public static String getToken() {
        HttpServletRequest httpServletRequest = tryGetHttpServletRequest();
        if (httpServletRequest == null) {
            throw new BusinessException("not support request", ResultCode.ERROR.getCode());
        }
        String token = tryGetToken(httpServletRequest);
        if (token == null) {
            throw new BusinessException(ResultCode.LOGIN_OFFLINE);
        }
        return token;
    }

    @Nullable
    public static String tryGetToken(@NonNull HttpServletRequest request) {
        String token = request.getParameter(Constant.AUTHENTICATION);
        if (token != null) {
            log.info("get token from parameter: {}", token);
            return token;
        }
         token = request.getHeader(Constant.AUTHENTICATION);
        if (token != null) {
            log.info("get token from header: {}", token);
            return token;
        }
        token = tryGetCookie(request, Constant.AUTHENTICATION);
        log.info("get token from cookie: {}", token);
        return token;
    }

    public static User currUser() {
        String token = getToken();
        String account = JwtUtil.getUserAccount(token);
        String publicKey = JwtUtil.tryGetPublicKey(token);
        return new User(token, account, publicKey);
    }

    public record User(String token, String account, String publicKey){}


    public static String trackId() {
        HttpServletRequest httpServletRequest = tryGetHttpServletRequest();
        if (httpServletRequest != null) {
            return httpServletRequest.getHeader(Constant.RequestId);
        }
        return null;
    }

}
